package com.bum.sock.poll.bo;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.comet.CometEvent;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;

import com.bum.sock.poll.om.Message;
import com.bum.sock.poll.om.Message.Receiver;

/**
 * Poller class.
 */
public class MessageBO implements Runnable {
	protected boolean									running		= true;

	private static final ConcurrentLinkedQueue<Message>	messages	= new ConcurrentLinkedQueue<Message>();

	private static final Integer						TIMEOUT		= 45 * 1000;

	private EventBO										eventBO;

	List<MessageEventListener>							listeners	= new LinkedList<MessageBO.MessageEventListener>();

	public void addMessageEventListener(MessageEventListener listener) {
		listeners.add(listener);
	}

	public interface MessageEventListener {
		void onEventEnd(String wsc, String id);
	}

	public void destroy() {
		running = false;
		eventBO.destroy();
		synchronized (messages) {
			messages.notify();
		}
	}

	public void begin(CometEvent event) throws IOException {
		HttpServletRequest request = event.getHttpServletRequest();
		String id = request.getParameter("id");
		if (!IdBO.checkId(id)) {
			event.close();
			return;
		}

		String sessionId = request.getParameter("wsc");
		System.out.println("begin " + sessionId);

		request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
		eventBO.add(event, id, sessionId);

		HttpServletResponse response = event.getHttpServletResponse();
		response.setContentType("text/html; charset=UTF-8");
		PrintWriter writer = response.getWriter();

		writer.println("<html><body onload='location.href=location.href'><script>window.parent." + getCallback(event) + "(\"ping\", '*');</script>");
		writer.flush();
	}

	private String getCallback(CometEvent event) {
		String callback = event.getHttpServletRequest().getParameter("callback");
		if (StringUtils.isBlank(callback)) {
			callback = "postMessage";
		}
		return callback;
	}

	private void remove(CometEvent event) {
		HttpServletRequest request = event.getHttpServletRequest();
		String id = request.getParameter("id");
		eventBO.remove(event, id, request.getParameter("wsc"));
	}
	
	public void remove(String sessionId) {
		eventBO.remove(sessionId);
	}

	public void end(CometEvent event) throws IOException {
		if (CollectionUtils.isNotEmpty(listeners)) {
			HttpServletRequest request = event.getHttpServletRequest();
			String id = request.getParameter("id");
			String wsc = request.getParameter("wsc");
			System.out.println("End " + wsc);
			
			for (MessageEventListener listener : listeners) {
				listener.onEventEnd(wsc, id);
			}
		}

		PrintWriter writer = event.getHttpServletResponse().getWriter();
		writer.println("</body></html>");
		event.close();

		remove(event);
	}

	public void error(CometEvent event) throws IOException {
		System.out.println("Error");
		remove(event);
		event.close();
	}

	public void send(Message message) {
		synchronized (messages) {
			messages.add(message);
			messages.notify();
		}
	}

	/**
	 * The background thread that listens for incoming TCP/IP connections and hands them off to an appropriate processor.
	 */
	@Override
	public void run() {

		// Loop until we receive a shutdown command
		while (running) {
			Message[] pendingMessages;
			synchronized (messages) {
				try {
					if (messages.size() == 0) {
						messages.wait();
					}
				} catch (InterruptedException e) {
					// Ignore
				}
				pendingMessages = messages.toArray(new Message[0]);
				messages.clear();
			}

			for (int j = 0; j < pendingMessages.length; j++) {
				Message message = pendingMessages[j];
				for (Receiver rec : message.receivers) {
					Map<String, CometEvent> events = eventBO.get(rec.user);
					if (MapUtils.isEmpty(events)) {
						continue;
					}

					if (rec.id != null) {
						CometEvent event = events.get(rec.id);
						if (event != null) {
							try {
								PrintWriter writer = event.getHttpServletResponse().getWriter();
								writer.println("<script>window.parent." + getCallback(event) + "(" + message.json + ", '*');</script>");
								writer.flush();
							} catch (Exception e) {
							}
						}
					} else {
						for (CometEvent event : events.values()) {
							try {
								PrintWriter writer = event.getHttpServletResponse().getWriter();
								writer.println("<script>window.parent." + getCallback(event) + "(" + message.json + ", '*');</script>");
								writer.flush();
							} catch (Exception e) {
							}
						}

					}
				}
			}

		}

	}

	public void setEventBO(EventBO eventBO) {
		this.eventBO = eventBO;
	}

}
